{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Transformers 설치 방법\n",
    "! pip install transformers datasets evaluate accelerate\n",
    "# 마지막 릴리스 대신 소스에서 설치하려면, 위 명령을 주석으로 바꾸고 아래 명령을 해제하세요.\n",
    "# ! pip install git+https://github.com/huggingface/transformers.git"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 🤗 PEFT로 어댑터 가져오기 [[load-adapters-with-peft]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Parameter-Efficient Fine Tuning (PEFT)](https://huggingface.co/blog/peft) 방법은 사전훈련된 모델의 매개변수를 미세 조정 중 고정시키고, 그 위에 훈련할 수 있는 매우 적은 수의 매개변수(어댑터)를 추가합니다. 어댑터는 작업별 정보를 학습하도록 훈련됩니다. 이 접근 방식은 완전히 미세 조정된 모델에 필적하는 결과를 생성하면서, 메모리 효율적이고 비교적 적은 컴퓨팅 리소스를 사용합니다.\n",
    "\n",
    "또한 PEFT로 훈련된 어댑터는 일반적으로 전체 모델보다 훨씬 작기 때문에 공유, 저장 및 가져오기가 편리합니다.\n",
    "\n",
    "<div class=\"flex flex-col justify-center\">\n",
    "  <img src=\"https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/peft/PEFT-hub-screenshot.png\"/>\n",
    "  <figcaption class=\"text-center\">Hub에 저장된 OPTForCausalLM 모델의 어댑터 가중치는 최대 700MB에 달하는 모델 가중치의 전체 크기에 비해 약 6MB에 불과합니다.</figcaption>\n",
    "</div>\n",
    "\n",
    "🤗 PEFT 라이브러리에 대해 자세히 알아보려면 [문서](https://huggingface.co/docs/peft/index)를 확인하세요."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 설정 [[setup]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🤗 PEFT를 설치하여 시작하세요:\n",
    "\n",
    "```bash\n",
    "pip install peft\n",
    "```\n",
    "\n",
    "새로운 기능을 사용해보고 싶다면, 다음 소스에서 라이브러리를 설치하는 것이 좋습니다:\n",
    "\n",
    "```bash\n",
    "pip install git+https://github.com/huggingface/peft.git\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 지원되는 PEFT 모델 [[supported-peft-models]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🤗 Transformers는 기본적으로 일부 PEFT 방법을 지원하며, 로컬이나 Hub에 저장된 어댑터 가중치를 가져오고 몇 줄의 코드만으로 쉽게 실행하거나 훈련할 수 있습니다. 다음 방법을 지원합니다:\n",
    "\n",
    "- [Low Rank Adapters](https://huggingface.co/docs/peft/conceptual_guides/lora)\n",
    "- [IA3](https://huggingface.co/docs/peft/conceptual_guides/ia3)\n",
    "- [AdaLoRA](https://huggingface.co/papers/2303.10512)\n",
    "\n",
    "🤗 PEFT와 관련된 다른 방법(예: 프롬프트 훈련 또는 프롬프트 튜닝) 또는 일반적인 🤗 PEFT 라이브러리에 대해 자세히 알아보려면 [문서](https://huggingface.co/docs/peft/index)를 참조하세요."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PEFT 어댑터 가져오기 [[load-a-peft-adapter]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "🤗 Transformers에서 PEFT 어댑터 모델을 가져오고 사용하려면 Hub 저장소나 로컬 디렉터리에 `adapter_config.json` 파일과 어댑터 가중치가 포함되어 있는지 확인하십시오. 그런 다음 `AutoModelFor` 클래스를 사용하여 PEFT 어댑터 모델을 가져올 수 있습니다. 예를 들어 인과 관계 언어 모델용 PEFT 어댑터 모델을 가져오려면 다음 단계를 따르십시오:\n",
    "\n",
    "1. PEFT 모델 ID를 지정하십시오.\n",
    "2. [AutoModelForCausalLM](https://huggingface.co/docs/transformers/main/ko/model_doc/auto#transformers.AutoModelForCausalLM) 클래스에 전달하십시오."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoModelForCausalLM, AutoTokenizer\n",
    "\n",
    "peft_model_id = \"ybelkada/opt-350m-lora\"\n",
    "model = AutoModelForCausalLM.from_pretrained(peft_model_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<Tip>\n",
    "\n",
    "`AutoModelFor` 클래스나 기본 모델 클래스(예: `OPTForCausalLM` 또는 `LlamaForCausalLM`) 중 하나를 사용하여 PEFT 어댑터를 가져올 수 있습니다.\n",
    "\n",
    "</Tip>\n",
    "\n",
    "`load_adapter` 메소드를 호출하여 PEFT 어댑터를 가져올 수도 있습니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoModelForCausalLM, AutoTokenizer\n",
    "\n",
    "model_id = \"facebook/opt-350m\"\n",
    "peft_model_id = \"ybelkada/opt-350m-lora\"\n",
    "\n",
    "model = AutoModelForCausalLM.from_pretrained(model_id)\n",
    "model.load_adapter(peft_model_id)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8비트 또는 4비트로 가져오기 [[load-in-8bit-or-4bit]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`bitsandbytes` 통합은 8비트와 4비트 정밀도 데이터 유형을 지원하므로 큰 모델을 가져올 때 유용하면서 메모리도 절약합니다. 모델을 하드웨어에 효과적으로 분배하려면 [from_pretrained()](https://huggingface.co/docs/transformers/main/ko/main_classes/model#transformers.PreTrainedModel.from_pretrained)에 `load_in_8bit` 또는 `load_in_4bit` 매개변수를 추가하고 `device_map=\"auto\"`를 설정하세요:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig\n",
    "\n",
    "peft_model_id = \"ybelkada/opt-350m-lora\"\n",
    "model = AutoModelForCausalLM.from_pretrained(peft_model_id, quantization_config=BitsAndBytesConfig(load_in_8bit=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 새 어댑터 추가 [[add-a-new-adapter]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "새 어댑터가 현재 어댑터와 동일한 유형인 경우에 한해 기존 어댑터가 있는 모델에 새 어댑터를 추가하려면 `add_adapter`를 사용할 수 있습니다. 예를 들어 모델에 기존 LoRA 어댑터가 연결되어 있는 경우:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoModelForCausalLM, OPTForCausalLM, AutoTokenizer\n",
    "from peft import PeftConfig\n",
    "\n",
    "model_id = \"facebook/opt-350m\"\n",
    "model = AutoModelForCausalLM.from_pretrained(model_id)\n",
    "\n",
    "lora_config = LoraConfig(\n",
    "    target_modules=[\"q_proj\", \"k_proj\"],\n",
    "    init_lora_weights=False\n",
    ")\n",
    "\n",
    "model.add_adapter(lora_config, adapter_name=\"adapter_1\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "새 어댑터를 추가하려면:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# attach new adapter with same config\n",
    "model.add_adapter(lora_config, adapter_name=\"adapter_2\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "이제 `set_adapter`를 사용하여 어댑터를 사용할 어댑터로 설정할 수 있습니다:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# use adapter_1\n",
    "model.set_adapter(\"adapter_1\")\n",
    "output = model.generate(**inputs)\n",
    "print(tokenizer.decode(output_disabled[0], skip_special_tokens=True))\n",
    "\n",
    "# use adapter_2\n",
    "model.set_adapter(\"adapter_2\")\n",
    "output_enabled = model.generate(**inputs)\n",
    "print(tokenizer.decode(output_enabled[0], skip_special_tokens=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 어댑터 활성화 및 비활성화 [[enable-and-disable-adapters]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "모델에 어댑터를 추가한 후 어댑터 모듈을 활성화 또는 비활성화할 수 있습니다. 어댑터 모듈을 활성화하려면:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from transformers import AutoModelForCausalLM, OPTForCausalLM, AutoTokenizer\n",
    "from peft import PeftConfig\n",
    "\n",
    "model_id = \"facebook/opt-350m\"\n",
    "adapter_model_id = \"ybelkada/opt-350m-lora\"\n",
    "tokenizer = AutoTokenizer.from_pretrained(model_id)\n",
    "text = \"Hello\"\n",
    "inputs = tokenizer(text, return_tensors=\"pt\")\n",
    "\n",
    "model = AutoModelForCausalLM.from_pretrained(model_id)\n",
    "peft_config = PeftConfig.from_pretrained(adapter_model_id)\n",
    "\n",
    "# to initiate with random weights\n",
    "peft_config.init_lora_weights = False\n",
    "\n",
    "model.add_adapter(peft_config)\n",
    "model.enable_adapters()\n",
    "output = model.generate(**inputs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "어댑터 모듈을 비활성화하려면:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.disable_adapters()\n",
    "output = model.generate(**inputs)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## PEFT 어댑터 훈련 [[train-a-peft-adapter]]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "PEFT 어댑터는 [Trainer](https://huggingface.co/docs/transformers/main/ko/main_classes/trainer#transformers.Trainer) 클래스에서 지원되므로 특정 사용 사례에 맞게 어댑터를 훈련할 수 있습니다. 몇 줄의 코드를 추가하기만 하면 됩니다. 예를 들어 LoRA 어댑터를 훈련하려면:\n",
    "\n",
    "<Tip>\n",
    "\n",
    "[Trainer](https://huggingface.co/docs/transformers/main/ko/main_classes/trainer#transformers.Trainer)를 사용하여 모델을 미세 조정하는 것이 익숙하지 않다면 [사전훈련된 모델을 미세 조정하기](https://huggingface.co/docs/transformers/main/ko/training) 튜토리얼을 확인하세요.\n",
    "\n",
    "</Tip>\n",
    "\n",
    "1. 작업 유형 및 하이퍼파라미터를 지정하여 어댑터 구성을 정의합니다. 하이퍼파라미터에 대한 자세한 내용은 `LoraConfig`를 참조하세요."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from peft import LoraConfig\n",
    "\n",
    "peft_config = LoraConfig(\n",
    "    lora_alpha=16,\n",
    "    lora_dropout=0.1,\n",
    "    r=64,\n",
    "    bias=\"none\",\n",
    "    task_type=\"CAUSAL_LM\",\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "2. 모델에 어댑터를 추가합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.add_adapter(peft_config)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "3. 이제 모델을 [Trainer](https://huggingface.co/docs/transformers/main/ko/main_classes/trainer#transformers.Trainer)에 전달할 수 있습니다!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "trainer = Trainer(model=model, ...)\n",
    "trainer.train()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "훈련한 어댑터를 저장하고 다시 가져오려면:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "model.save_pretrained(save_dir)\n",
    "model = AutoModelForCausalLM.from_pretrained(save_dir)"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 4
}
